package com.itaming.lycheeframework.support.utils;

import lombok.experimental.UtilityClass;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.util.Assert;

/**
 * Spring工具类
 *
 * @author A.Ming
 */
@UtilityClass
public class SpringUtil {

    /**
     * 获取Bean
     *
     * @param beanClass Bean类型
     * @return Bean对象
     * @throws BeansException BeansException
     */
    public static <T> T getBean(Class<T> beanClass) throws BeansException {
        return getContext().getBean(beanClass);
    }

    /**
     * 获取Bean
     *
     * @param beanName Bean名称
     * @return Bean对象
     * @throws BeansException BeansException
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String beanName) throws BeansException {
        return (T) getContext().getBean(beanName);
    }

    /**
     * 获取Bean
     *
     * @param beanName  Bean名称
     * @param beanClass Bean类型
     * @return Bean对象
     * @throws BeansException BeansException
     */
    public static <T> T getBean(String beanName, Class<T> beanClass) throws BeansException {
        return getContext().getBean(beanName, beanClass);
    }

    /**
     * 发布事件
     *
     * @param event 事件
     */
    public static void publishEvent(ApplicationEvent event) {
        getContext().publishEvent(event);
    }

    /**
     * 获取Spring上下文
     *
     * @return Spring上下文
     */
    public static ApplicationContext getContext() {
        return ContextHolder.getContext();
    }

    /**
     * ContextHolder
     * <p>
     * 用于存储和提供Spring上下文，
     * 该内部类的设计目的是隔离SpringUtil的核心功能（如获取Bean和发布事件）与上下文存储逻辑，
     * 避免工具类对外暴露与使用者无关的细节
     * <p>
     * 初始化方法 {@link #setApplicationContext} 应仅在启动时由配置类调用，外部其他地方不应直接使用
     */
    public static class ContextHolder {

        /**
         * ApplicationContext
         */
        private static ApplicationContext context;

        /**
         * 初始化Spring上下文
         *
         * @param applicationContext Spring上下文
         */
        public static void setApplicationContext(ApplicationContext applicationContext) {
            if (context != null) {
                throw new IllegalStateException("ApplicationContext is already initialized");
            }
            context = applicationContext;
        }

        /**
         * 获取Spring上下文
         *
         * @return Spring上下文
         */
        private static ApplicationContext getContext() {
            Assert.state(context != null, "ApplicationContext is not initialized");
            return context;
        }

    }

}
